
namespace Behemoth.SharePoint.CustomFieldTypes {

    using System;
    using System.Collections.Generic;
    using System.Text;

    using Microsoft.SharePoint;

	public enum DelimitedFieldValueIndexes {
		Delimiter,
		IsValueEncoded,
		Items
	}

	public class DelimitedFieldValue : SPFieldMultiColumnValue {

		private const int NUM_SUBCOLUMNS = 3;

		public readonly static string[] Delimiters = new string[] { 
			", ", "; "
		};

		public string this[DelimitedFieldValueIndexes index] {
			get { return base[(int)index]; }
			set { base[(int)index] = value; }
		}

		private List<string> _items;
		public List<string> Items {
			get {
				if (_items == null)
					_items = new List<string>();
				return _items;
			}
		}

		private string _delimiter {
			get { return this[DelimitedFieldValueIndexes.Delimiter]; }
			set { this[DelimitedFieldValueIndexes.Delimiter] = value; }
		}
		public string ItemsDelimiter {
			get { return _delimiter; }
		}

		private bool _valueEncoded {
			get {
				bool value;
				bool success = bool.TryParse( this[DelimitedFieldValueIndexes.IsValueEncoded], out value );
				return success ? value : false;
			}
			set { this[DelimitedFieldValueIndexes.IsValueEncoded] = value.ToString(); }
		}
		public bool IsValueEncoded {
			get { return _valueEncoded; }
		}

		public DelimitedFieldValue( string multiColumnValue )
			: base( multiColumnValue ) {
			if (this.Count != NUM_SUBCOLUMNS)
				throw new IndexOutOfRangeException( "The string passed to DelimitedFieldValue had the wrong number of sub-columns." );
			string delimitedValues = this[DelimitedFieldValueIndexes.Items];
			SetItems( delimitedValues );
		}
		public DelimitedFieldValue( string delimiter, bool isEncoded, string delimitedValues )
			: this( delimiter, isEncoded ) {
			SetItems( delimitedValues );
		}
		public DelimitedFieldValue( string delimiter, bool isEncoded )
			: base( NUM_SUBCOLUMNS ) {
			this._valueEncoded = isEncoded;
			this._delimiter = delimiter;
		}

		private void SetItems( string delimitedValues ) {
			string[] values = delimitedValues.Split( new string[] { _delimiter }, StringSplitOptions.None );
			Items.Clear();
			Items.AddRange( values );
			if (this._valueEncoded)
				DecodeValues( this._delimiter );
		}

		public bool ValueContainsDelimiter() {
			return ValueContainsDelimiter( this._delimiter );
		}
		public bool ValueContainsDelimiter(string delim) {
			foreach (string value in Items) {
				if (value.Contains(this._delimiter))
					return true;
			}
			return false;
		}
		public void ChooseDelimiter() {
			foreach (string delim in Delimiters) {
				if (!ValueContainsDelimiter(delim)) {
					this._delimiter = delim;
					return;
				}
			}
			// try the same with the values encoded
			foreach (string delim in Delimiters) {
				EncodeValues( delim );
				if (!ValueContainsDelimiter( delim )) {
					this._delimiter = delim;
					return;
				}
				DecodeValues( delim );
			}
			throw new NotSupportedException("The lis tof available delimiters has been exhausted, because the value collection contains all the available strings.");
		}

		/// <summary>
		/// Cycle through the values and replace any spaces that would
		/// prevent the list from being saved.
		/// </summary>
		/// <param name="delim"></param>
		private void EncodeValues( string delim ) {
			string replaceDelim = delim.Replace( " ", "&nbsp;" );
			for (int i = 0; i < Items.Count; i++) {
				if (Items[i].Contains( delim )) {
					Items[i] = Items[i].Replace( delim, replaceDelim );
					_valueEncoded = true;
				}
			}
		}
		/// <summary>
		/// Undoes the effect of EncodeValues so that strings can be
		/// read naturally.
		/// </summary>
		/// <param name="delim"></param>
		private void DecodeValues( string delim ) {
			if (!_valueEncoded)
				return;
			string replaceDelim = delim.Replace( " ", "&nbsp;" );
			for (int i = 0; i < this.Count; i++) {
				if (Items[i].Contains( replaceDelim )) {
					Items[i] = Items[i].Replace( replaceDelim, delim );
				}
			}
			_valueEncoded = false;
		}

		public override string ToString() {
			UpdateItemsString();
			return base.ToString();
		}

		public void UpdateItemsString() {
			ChooseDelimiter();
			string[] values = Items.ToArray();
			string delimitedValues = string.Join( this._delimiter, values );
			this[DelimitedFieldValueIndexes.Items] = delimitedValues;
		}
		public string GetItemsString(bool update) {
			UpdateItemsString();
			return this[DelimitedFieldValueIndexes.Items];
		}

	} // class

} // namespace
